#include "generate_designator.h"

Symbol getDesignatorSymbol(ParseTree::Name ns, ParseTree::Designator &designator)
{    
    ParseTree::Name name(1, *designator.identifier);
    // TODO primitives
    
    if (designator.identifier->identifier == "this")
        name = getThisClass(ns);
    else
        name = namespaceLookupFQN(ns, name);
    Symbol s = symbolTable[name];
    // TODO arrays (dereferenced vs array types)
    for(unsigned int i = 0; i < designator.rest.size(); i++)
    {
        if (designator.rest[i].identRestType == ParseTree::IdentRest::Ident)
        {
            ParseTree::Name newName = s.type.userType;
            newName.push_back(*designator.rest[i].identifier);
            newName = namespaceLookupFQN(ns, newName);
            s = symbolTable[newName];
        }
    }
    
    return s;
}

void identRestCast(ParseTree::Name ns, Symbol &parent, ParseTree::IdentRest &rest, string &c)
{
    if (c.substr(0, 1) == "*")
        c = c.substr(1);
    parent.type.typeType = ParseTree::Type::UserType;
    if (parent.type.primitiveType == ParseTree::Type::Int)
    {
        c = "" + symbolTable[toName("Thinder.Prim.Integer.Integer")].internal + "(" + c + ")";
        parent.type.userType = toName("Thinder.Prim.Integer");
    }
}

void generateIdentRest(ParseTree::Name ns, Symbol &parent, ParseTree::IdentRest &rest, string &c, bool last, ParseTree::Type typeHint)
{
    //std::cout << "namespace: " << printName(ns) << std::endl;
    if (rest.identRestType == ParseTree::IdentRest::Ident)
    {
        // TODO cast if typehint want an object instead of a primitive
        if (parent.type.typeType == ParseTree::Type::PrimitiveType && !last)
            identRestCast(ns, parent, rest, c);
        ParseTree::Name newName = parent.type.userType;
        newName.push_back(*rest.identifier);
        //std::cout << "newname1: " << printName(newName) << std::endl;
        newName = namespaceLookupFQN(ns, newName);
        //std::cout << "newname2: " << printName(newName) << std::endl;

        Symbol s = symbolTable[newName];
        if (s.symbolType == Symbol::Var)
        {
            c = "" + c + "->" + s.internal + "";
        }
        if (s.symbolType == Symbol::Func)
        {
            // do nothing (yet), this will be done in the next iteration
        }
        parent = s;
    }
    if (rest.identRestType == ParseTree::IdentRest::Func)
    {
        if (c.length() > 1 && c.substr(0, 1) == "*")
            c = c.substr(1);
            
        // find out what overload function we need.
        ParseTree::Name funcname = findSymbolName(parent);
        //std::cout<< "symbol:" << printSymbol(symbolTable[funcname]) <<std::endl;
        
        // TODO: refactor this into a overload of findOverload
        ParseTree::Declaration declaration;
        declaration.declType = ParseTree::Declaration::FunctionDecl;
        declaration.type = typeHint;
        vector<ParseTree::FormPar> argumentTypes;
        for (unsigned int i = 0; i < rest.funcArguments.size(); i++)
        {
            ParseTree::FormPar fp;
            fp.type = getTypeOfExpression(ns, rest.funcArguments[i]);
            argumentTypes.push_back(fp);
        }
        declaration.formPars = argumentTypes;
        parent = symbolTable[findOverload(ns, funcname, declaration)];
        
        c = "" + parent.internal + "(" + c; 
        for (unsigned int i = 0; i < rest.funcArguments.size(); i++)
        {
            c += generateExpression(ns, rest.funcArguments[i], parent.argumentTypes[i]);  
            if (i < rest.funcArguments.size() - 1)
                c += ", ";
        }
        c += ")";    
        if (parent.type.typeType == ParseTree::Type::PrimitiveType && !last)
            identRestCast(ns, parent, rest, c);
    }
    if (rest.identRestType == ParseTree::IdentRest::Array)
    {
        if (c.length() > 1)
            c = "&(" + c.substr(1);
        c += "[*" + generateExpression(ns, *rest.arrayExpr, typeHint) + "]";
    }
}

string generateDesignator(ParseTree::Name ns, ParseTree::Designator &designator, ParseTree::Type typeHint)
{
    //std::cout << "\nnew designator: " << designator.identifier->identifier << std::endl;
    string c = "";
    ParseTree::Name name(1, *designator.identifier);
    Symbol s;
    if (designator.identifier->identifier == "this")
    {
        c += "this";
        name = getThisClass(ns);
        s = symbolTable[name];
    } else {
        name = namespaceLookupFQN(ns, name);
        //std::cout <<"found name: " << printName(name) << std::endl;
        s = symbolTable[name];
        //std::cout << "usertype: " << printType(s.type) << std::endl;
        
        // TODO: cast if designator is primitive and typehint is userType (like Integer)
        // Maybe this already works (check)
        
        if (s.symbolType == Symbol::Var)
        {
            c += "(" ;
            if (isFieldOfThisClass(ns, name))
                c += "this->";
            c += s.internal + ")";
        } else if (s.symbolType == Symbol::Func)
        {
            if (isMethodOfThisClass(ns, name))
                c = "this";
        }
    }
    
    for(unsigned int i = 0; i < designator.rest.size(); i++)
    {
        generateIdentRest(ns, s, designator.rest[i], c, i == designator.rest.size() - 1, typeHint);
    }
    if (c.substr(0, 1) == "*")
        c = c.substr(1); // always return a pointer
    return c; 
}

